import numpy as np
import sys,os,inspect
from PyQt4 import QtCore

from KModel.KSingleStock import KSingleStock
from KTrade.KStockFeed import KStockFeed
from KIndicators import KBaseIndicator
from PyQt4.QtGui import QGraphicsObject,QPen,QBrush,QColor,QPolygonF
from PyQt4 import QtCore,QtGui,Qt
from PyQt4.QtCore import QPointF
from KApp import KConstants as vm
class KAtomGartleyPattern(KBaseIndicator.KBaseIndicator):
	''' the atomic indicator to search for C Miner Patterns given peaks'''
	def __init__(self,parent,peakindicator): #need pass in peak indicator to work
		super(KAtomGartleyPattern,self).__init__(parent)
		self.peakindictor=peakindicator
	def GetPatternList(self):
		return self.patternlist
	def GetPredictList(self):
		if hasattr(self,'predictlist'):
			return self.predictlist
		else:
			return None

	def GetLogicRange(self):
		# get the range of all data in this indicator
		if self.predictlist:
			(pxrange,pyrange)=self.m_stock.CurWindowRange('Price')	
			minprice=None
			maxprice=None
			for pindex in range(0,len(self.predictlist)):
				lastpointX=self.patternlist[pindex][1][-2][0] # check the x of point B( in gartley) or  D ( in 3way)
				firstpointX=self.patternlist[pindex][1][0][0] # check the x of point X
				if firstpointX>=pxrange[0] and lastpointX<=pxrange[1]:
					curpreds=self.predictlist[pindex][0]
					#print(curpreds)
					if not maxprice or max(curpreds)>maxprice:
						maxprice=max(curpreds)
					if not minprice or min(curpreds)<=minprice:
						minprice=min(curpreds)
			if minprice and maxprice:
				return (pxrange,[minprice,maxprice])
		return self.m_stock.CurWindowRange('Price') # else return default



	def calculate(self,**kargs):		
		
		
		
		#narray=self.m_stock.GetNumpyArray()
		#peaks=self.peakindictor.GetPeakList()
		#windowsize=self.peakindictor.GetWindowSize()
		#self.patternlist=KAtomGartleyPattern.FindGartleyABCConsolidating(narray,peaks,windowsize)
		#self.predictlist=KAtomGartleyPattern.PredictGartleyTargetPrice(self.patternlist)
		#self.patternlist=KAtomGartleyPattern.Find3WayDriveTrend(narray,peaks,windowsize)
		#self.predictlist=KAtomGartleyPattern.Predict3WayDriveTargetPrice(self.patternlist)

		self.OnStart()
		self.stockfeed=KStockFeed(self.m_stock)
		stockfeed=self.stockfeed
		while stockfeed.HasNextBar():
			stockfeed.MoveForward()
			self.OnNewBars(stockfeed.GetCurrentIndex())		
		self.OnFinish()
		
		return self.patternlist	
	
	## online functions
	def OnNewBars(self,curfeedindex):
		# used in online mode, will be called when a new bar is available
		if self.peakindictor.GetPeakCount()>self.curpeakcount:
			# new peak is availbale
			curbars=self.stockfeed.GetBarsTillNow()
			windowsize=self.peakindictor.GetWindowSize()
			if self.peakindictor.GetPeakCount()>=3:
				peaklist=self.peakindictor.GetPeakList()
				LatestPeaks=peaklist[-4:]
				# find pattern
				pattern=KAtomGartleyPattern.FindGartleyABCConsolidatingForPeak(curbars,LatestPeaks,windowsize)
				if pattern:
					self.patternlist.append(pattern)
					# predict target price of pattern
					predict=KAtomGartleyPattern.PredictGartleyTargetPriceForPattern(pattern)
					self.predictlist.append(predict)
					
		self.curpeakcount=self.peakindictor.GetPeakCount()
		
	def OnStart(self):
		# used in online mode, called before simulation started
		self.curpeakcount=0
		self.patternlist=[]
		self.predictlist=[]
		
	def OnFinish(self):
		# used in online mode, called after simulation exectued
		KBaseIndicator.KBaseIndicator.update(self)		
		

	@staticmethod
	def IsPotentialPatternEnd(closep,pattern):
		# detect if curpoint could be a target end of given pattern
		# first return denote is a potential C point or not
		# second return denote the direction of the pattern
		[Xpoint,Apoint,Bpoint,breakpoint]=pattern[1]
		
		if Bpoint[2]>0: #down trend consolidation
			if closep<Apoint[1]: # is a potential C point
				return (1,-1)
				
			elif closep>Bpoint[1]: # will invalidate the pattern
				return (-1,-1)
			else:
				return (0,-1)
			
		else: # up trend consolidation
			if closep>Apoint[1]:# is a potential C point
				return (1,1)
			elif closep<Bpoint[1]: # will invalidate the pattern
				return (-1,1)
			else:
				return (0,1)
			

	@staticmethod
	def GetPotentialCPoint(pattern, barstillnow):
		# search the potential Cpoint given available data till now.
		[Xpoint,Apoint,Bpoint,breakpoint]=pattern[1]
		return None

	@staticmethod
	def GetPatternRetracmentTargetPrice(pattern, CpointPrice):
		# get the target price of retracement, given  a candidate Cpiont.  will be used in stop loss setup
		[Xpoint,Apoint,Bpoint,breakpoint]=pattern[1]
		CLength=Bpoint[1]-CpointPrice
		Cratio=1-0.618
		CRetraceLength=CLength*Cratio
		targetprice=CRetraceLength+CpointPrice
		return targetprice
		
	@staticmethod
	def FindGartleyABCConsolidatingForPeak(narray,peaklist,windowsize):
	# search for ABC gartley pattern among all extremas

		npworklist=np.array(peaklist)
		if abs(np.diff(npworklist[:,2])).min()<0.5: # skip if any neighbor extrema has same sign
			return None
		Bpoint=npworklist[-1,:]
		Apoint=npworklist[-2,:]
		Xpoint=npworklist[-3,:]
		curpointindex=narray.shape[0]-1
		if Bpoint[2]>0: #down trend consolidation
			if not Bpoint[1]<Xpoint[1]: # B should not exceed A
				return None
			breakpoint=[curpointindex,narray[curpointindex,3],1] # record till which points lead to the recognition of this pattern
		else: # up trend consolidation
			if not Bpoint[1]>Xpoint[1]: # B should not exceed A
				return None
			breakpoint=[curpointindex,narray[curpointindex,3],-1]

		if breakpoint:
			return ['Gartley',[Xpoint,Apoint,Bpoint,breakpoint]] # recorgnize as valid pattern
		else:
			return None


	@staticmethod
	def PredictGartleyTargetPriceForPattern(each_pattern):
		# the problem is it is not easy to find 'last trend', see book <High Probability Trading>- P110
		Aratio=np.array([0.618,1.0,1.62])
		Bratio=np.array([1.27,1.62,2.62])
		SymbolTemplate=['A']*3+['B']*3

		[Xpoint,Apoint,Bpoint,breakpoint]=each_pattern[1]
		Alength=Xpoint[1]-Apoint[1]
		Blength=Bpoint[1]-Apoint[1]
		# 2nd map:  same way mapping of wave #1
		ASamewayMappingSeg=Alength*Aratio
		Apredict=Bpoint[1]-ASamewayMappingSeg
		# 3rd map:  outer retracemnt of wave #2
		BRevserRetraceMappingSeg=Blength*Bratio
		Bpredict=Bpoint[1]-BRevserRetraceMappingSeg
		AllPredict=np.concatenate((Apredict,Bpredict),axis=0).tolist()

		
		return [AllPredict,SymbolTemplate]
	
	

class KAtomGartleyPatternDisplayItem(KBaseIndicator.KBaseIndicatorDisplayItem):

	def PaintPattern(self,painter):
		if not hasattr(self,'model') :
			return
		if not self.model.IsReady():
			return 

		if self.model.IsResultExpired()==True:
			self.model.calcluate()

		# start painting		

		linepen=QPen(QColor(0,98,195))
		linepen.setStyle(QtCore.Qt.DashLine)
		linepen.setWidthF(2)

		textpen=QPen(QColor(255,0,0))
		textpen.setStyle(QtCore.Qt.DashLine)
		textpen.setWidthF(1)
		painter.setPen(linepen)

		painter.setFont(self.GetFont('labelText'))

		width=1.0/2.0
		#(test,())=self.viewmodel.mapLogiceSizeToScene((width,))
		[width],[]=self.viewmodel.mapLogiceSizeToScene(vm.RectType.ContentBound,[width])

		# only show extrema in current window
		XCordinates=self.m_stock.CurWindowData('Index')
		minx=min(XCordinates)
		maxx=max(XCordinates)
		patternlist=self.model.GetPatternList()
		predlist=self.model.GetPredictList()
		patternwindow=[]

		predwindow=[]
		for patternindex in range(0,len(patternlist)):
			each_pattern=patternlist[patternindex]
			if each_pattern[1][0][0]>minx and each_pattern[1][-1][0]<maxx:
				patternwindow.append(each_pattern)
				if predlist:
					predwindow.append(predlist[patternindex])

		patternwindow=[x[1] for x in patternlist if x[1][0][0]>minx and x[1][-1][0]<maxx]

		textlist=['X','A','B','C','D','E']
		for index in range(0,len(patternwindow)):

			# draw pattern
			npwindow=np.array(patternwindow[index])
			xcords=npwindow[:,0].tolist()
			ycords=npwindow[:,1].tolist()
			(xcords,ycords)=self.viewmodel.mapLogiceCordToScene(vm.RectType.ContentBound,xcords,ycords)
			for pindex in range(0,len(xcords)-1):
				startp=QPointF(xcords[pindex],ycords[pindex])
				endp=QPointF(xcords[pindex+1],ycords[pindex+1])
				textp=QPointF(xcords[pindex]+2*width,ycords[pindex])
				painter.setPen(linepen)
				painter.drawLine(startp,endp)	
				painter.setPen(textpen)
				painter.drawText(textp,textlist[pindex]) # print label
			pindex+=1
			textp=QPointF(xcords[pindex]+2*width,ycords[pindex])
			painter.drawText(textp,textlist[pindex]) # print potential pattern end points
			# draw prediction
			if predwindow:
				painter.setPen(linepen)
				predpoints=predwindow[index]
				([],predycords)=self.viewmodel.mapLogiceCordToScene(vm.RectType.ContentBound,[],predpoints[0])
				LastIndex=len(xcords)-2
				for index in range(0,len(predpoints[0])):
					if predpoints[1][index]=='A':
						linewidth=width
					else:
						linewidth=width*0.7
					startp=QPointF(xcords[LastIndex],predycords[index])
					endp=QPointF(xcords[LastIndex]+linewidth,predycords[index])
					painter.drawLine(startp,endp)


	def PrepareRenderPicture(self):
		## pre-computing a QPicture object allows paint() to run much more quickly, 
		## rather than re-drawing the shapes every time.

		try:
			if not hasattr(self,'model') :
				return
			if not self.model.IsReady():
				return 		
			if self.model.IsResultExpired()==True:
				self.model.calculate()	

			self.picture = QtGui.QPicture()
			painter = QtGui.QPainter(self.picture)		
			self.PaintPattern(painter)
		except:
			print('faild to render CMinderPattern')

		painter.end()	